#include "Reference.h"
#include "VariableWrapper.h"

Reference::Reference(REFERENCE_TYPE set_type) { 
    type = set_type;
    var = NULL;
    child = NULL;
    field = "";
}

VariableWrapper *Reference::getVar() { 
    if(this->var == NULL) {
        if(child != NULL) {
            return child->getVar();
        } else {
            return NULL;
        }
    }
    return this->var;
}

int Reference::getOrder() {
    if(child == NULL) return 0;
    if(type == POINTER) return 1 + child->getOrder();
    return 0;
}

int Reference::equals(Reference *other) {
    return this->getType() == other->getType() && equals_helper(other);
}
Reference *Reference::append(Reference *ref) {
    Reference *prev = NULL;
    Reference *curr = this;
    while(curr->child != NULL) {
        prev = curr;
        curr = curr->child;
    }
    if(prev == NULL) return ref;
    prev->setChild(ref);
    this->setVar(ref->getVar());
    return this;
}

int Reference::equals_helper(Reference *other) {
    if(other->getVar() != this->getVar()) return 0;
    if(other->getChild() == NULL && child != NULL) return 0;
    if(child == NULL && other->getChild() != NULL) return 0;
    if(child == NULL && other->getChild() == NULL) return 1;
    return other->getChild()->equals(child);
}

Reference *Reference::copy() {
    Reference *new_child = NULL;
    if(this->getChild() != NULL) {
        new_child = this->getChild()->copy();
    }
    Reference *tmp = new Reference(this->getType());
    tmp->setChild(new_child);
    tmp->setVar(this->getVar());
    tmp->setField(this->getField());
    return tmp;
}

string Reference::full_str() {
    string inter = this->full_str_helper();
    
    stringstream ss;
    ss<<"["<<this->getVar()->func_str()<<":"<<inter<<"]";
    VariableWrapper *var = this->getVar();
    Reference **aliases = var->getAliases();
    int n_aliases = var->num_aliases();
    
    
    if(n_aliases > 0) {
        ss<<" aka {";
        Reference *base_copy = this->copy();
        Reference *alias_copy = aliases[0]->copy();
        base_copy = base_copy->append(alias_copy);
        ss<<"["<<base_copy->getVar()->func_str()<<":"<<base_copy->full_str_helper()<<"]";
        for(int i = 1; i < n_aliases; i++) {
            Reference *base_copy = this->copy();
            Reference *alias_copy = aliases[i]->copy();
            base_copy = base_copy->append(alias_copy);
            ss<<", ["<<base_copy->getVar()->func_str()<<":"<<base_copy->full_str_helper()<<"]";
        }
        ss<<"}";
    }
    return ss.str();

    return "";
}

string Reference::full_str_helper() {
    switch(this->type) {
        case(POINTER):
        {
            stringstream ss;
            ss<<"(*"<<this->getChild()->full_str_helper()<<")";
            return ss.str();
        }
        case(STRUCT):
        {
            stringstream ss;
            ss<<"("<<this->getChild()->full_str_helper()<<"."<<field<<")";
            return ss.str();
        }
        case(VARIABLE):
        {
            return this->getVar()->str();
        }
    }
    return "";
}

